package com.zx.flow.db;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zx.flow.api.config.FlowConfig;
import com.zx.flow.api.execute.model.FlowContext;
import com.zx.flow.api.execute.model.FlowNode;
import com.zx.flow.api.execute.request.manager.ManagerFlowConfigListForm;
import com.zx.flow.api.execute.request.manager.ManagerFlowListForm;
import com.zx.flow.api.util.AssertUtil;
import com.zx.flow.api.util.Util;
import com.zx.flow.db.convert.DbConvert;
import com.zx.flow.db.dao.FlowConfigDao;
import com.zx.flow.db.dao.FlowContextDao;
import com.zx.flow.db.dao.FlowNodeDao;
import com.zx.flow.db.dao.FlowSecretDao;
import com.zx.flow.db.entity.FlowConfigEntity;
import com.zx.flow.db.entity.FlowContextEntity;
import com.zx.flow.db.entity.FlowNodeEntity;
import com.zx.flow.db.entity.FlowSecretEntity;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * 数据库存储
 */
@Component
@Slf4j
public class FlowStorageDBHandler implements FlowStorageHandler {

    @Autowired
    private FlowConfigDao flowConfigDao;
    @Autowired
    private FlowContextDao flowContextDao;
    @Autowired
    private FlowNodeDao flowNodeDao;
    @Autowired
    private FlowSecretDao secretDao;

    @Override
    public List<FlowSecretEntity> getSecret() {
        return this.secretDao.selectList(Wrappers.lambdaQuery());
    }

    @Override
    public IPage<FlowContext> getFlowList(ManagerFlowListForm form) {
        Page<FlowContextEntity> page = new Page<>(form.getPageNum(), form.getPageSize());
        this.flowContextDao.selectPage(page, Wrappers.<FlowContextEntity>lambdaQuery()
                .eq(ObjectUtils.isNotEmpty(form.getFlowId()), FlowContextEntity::getId, form.getFlowId())
                .eq(ObjectUtils.isNotEmpty(form.getFlowCode()), FlowContextEntity::getFlowConfigCode,
                        form.getFlowCode())
                .like(ObjectUtils.isNotEmpty(form.getLikeTitle()), FlowContextEntity::getTitle, form.getLikeTitle())
        );
        IPage<FlowContext> resPage = DbConvert.to(page, DbConvert::to);
        return resPage;
    }


    @Override
    public IPage<FlowConfig> getFlowConfigList(ManagerFlowConfigListForm form) {
        Page<FlowConfigEntity> page = new Page<>(form.getPageNum(), form.getPageSize());
        this.flowConfigDao.selectPage(page, Wrappers.<FlowConfigEntity>lambdaQuery()
                .eq(ObjectUtils.isNotEmpty(form.getFlowCode()), FlowConfigEntity::getFlowCode, form.getFlowCode())
                .like(ObjectUtils.isNotEmpty(form.getLikeName()), FlowConfigEntity::getFlowName, form.getLikeName()));
        IPage<FlowConfig> resPage = DbConvert.to(page, DbConvert::to);
        return resPage;
    }

    @Override
    public List<FlowConfig> getAllFlowConfig() {
        List<FlowConfigEntity> dbList = this.flowConfigDao.selectList(Wrappers.lambdaQuery());
        List<FlowConfig> resList = dbList.stream().map(db -> {
            FlowConfig config = Util.getJavaObject(db.getConfig(), FlowConfig.class);
            config.setEnable(db.getEnable());
            config.setVersion(db.getVersion());
            return config;
        }).collect(Collectors.toList());
        return resList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveFlowConfig(FlowConfig newConfig) {
        FlowConfigEntity db = this.flowConfigDao.selectOne(Wrappers.<FlowConfigEntity>lambdaQuery()
                .eq(FlowConfigEntity::getId, newConfig.getId()));

        String newId = ObjectUtils.isNotEmpty(newConfig.getId()) ? Util.uniqueId(newConfig) : newConfig.getId();
        // 新增配置
        if (db == null) {
            log.info("saveFlowConfig, 配置不存在，新增配置, newConfig={}", newConfig.toString());
            FlowConfigEntity newDb = new FlowConfigEntity();
            newDb.setId(newId);
            newDb.setFlowCode(newConfig.getFlowCode());
            newDb.setFlowName(newConfig.getFlowName());
            newDb.setEnable(true);
            newDb.setVersion(1);
            newDb.setConfig(Util.getJsonStr(newConfig));
            this.flowConfigDao.insert(newDb);
            return;
        }
        // 没有变化不用更新
        if (Objects.equals(newId, db.getId())) {
            log.info("saveFlowConfig, 配置没有变化, newConfig={}", newConfig.toString());
            return;
        }
        // 发生变化
        log.info("saveFlowConfig, 配置变化, 更新配置, newConfig={}", newConfig.toString());
        FlowConfigEntity newDb = DbConvert.to(newConfig);
        newDb.setVersion(newConfig.getVersion() + 1);
        this.flowConfigDao.updateById(newDb);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveFlow(FlowContext newflow) {
        if (null == getFlow(newflow.getId())) {
            log.info("saveFlow, 流程不存在, 新增流程, newConfig={}", newflow.toString());
            this.flowContextDao.insert(DbConvert.to(newflow));
        } else {
            log.info("saveFlow, 更新流程, newConfig={}", newflow.toString());
            this.flowContextDao.updateById(DbConvert.to(newflow));
        }
    }

    @Override
    public FlowContext getFlow(String flowId) {
        FlowContextEntity db = this.flowContextDao.selectOne(
                Wrappers.<FlowContextEntity>lambdaQuery().eq(FlowContextEntity::getId, flowId));
        if (db == null) {
            return null;
        }
        FlowContext resFlow = DbConvert.to(db);
        List<FlowNode> nodes = this.getNodes(flowId);
        resFlow.setNodes(nodes);
        return resFlow;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveNode(String flowId, FlowNode flowNode) {
        FlowContext flow = this.getFlow(flowId);
        AssertUtil.checkFlowNotNull(flow, flowId);
        FlowNodeEntity newDb = DbConvert.to(flowNode);
        FlowNode node = this.getNode(flowId, flowNode.getId());
        if (node == null) {
            this.flowNodeDao.insert(newDb);
        } else {
            this.flowNodeDao.updateById(newDb);
        }
    }

    @Override
    public List<FlowNode> getNodes(String flowId) {
        List<FlowNodeEntity> dbs = this.flowNodeDao.selectList(
                Wrappers.<FlowNodeEntity>lambdaQuery()
                        .eq(FlowNodeEntity::getFlowId, flowId)
                        .orderByAsc(FlowNodeEntity::getNodeIdx));
        List<FlowNode> resList = dbs.stream().map(DbConvert::to).collect(Collectors.toList());
        return resList;
    }

    @Override
    public FlowNode getNode(String flowId, String nodeId) {
        for (FlowNode node : getNodes(flowId)) {
            if (Objects.equals(node.getId(), nodeId)) {
                return node;
            }
        }
        return null;
    }

}
